#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <system_error>
#include <functional>

#include <sys/epoll.h>

#include "Sock.hpp"
#include "Erro.hpp"
#include "Log.hpp"

namespace epoll_ns
{

    const int default_port = 8080;
    const int EPOLL_SIZE = 128;
    const int default_fd = -1;
    const int default_num = 128;

    using func_t = std::function<std::string(const std::string&)>;

    class EpollServer
    {
    public:
        EpollServer(func_t func, uint16_t port = default_port)
            : _port(port), _listensock(default_fd), _epfd(default_fd), _revs_size(default_num), _revs(nullptr), _func(func)
        {}

        ~EpollServer()
        {
            if (_listensock != default_fd)
                close(_listensock);
            if (_epfd != default_fd)
                close(_epfd);
            if (_revs)
            {
                delete[] _revs;
                _revs = nullptr;
            }
        }

        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);

            _epfd = epoll_create(EPOLL_SIZE);
            if (_epfd < 0)
            {
                try {
                    throw std::system_error(errno, std::system_category());
                }
                catch (std::system_error& erro) {
                    logMessage(FATAL, "epoll create fail: %s", erro.what());
                    exit(EPOLL_CREATE_ERROR);
                }
            }

            logMessage(DEBUG, "epfd create succes: %d", _epfd);

            struct epoll_event event;
            event.events = EPOLLIN;
            event.data.fd = _listensock;
            epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &event);

            _revs = new struct epoll_event[_revs_size];

            logMessage(NORMAL, "server init success");
        }

        void start()
        {
            int timewait = -1;
            while (true)
            {
                int n = epoll_wait(_epfd, _revs, _revs_size, timewait);
                std::system_error erro(errno, std::generic_category());
                switch (n)
                {
                case 0: // 超时返回
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1: // 错误返回
                    logMessage(WARNING, "epoll_wait err, errCode: %d, %s", erro.code().value(), erro.what());
                    break;
                default: //有多少 IO fd 就绪
                    logMessage(NORMAL, "have event ready...");
                    HandlerEvent(n);
                    break;
                }
                sleep(1);
            }
            
        }

        void HandlerEvent(int ready_num)
        {
            for (int i = 0; i<ready_num; ++i)
            {
                uint32_t event = _revs[i].events;
                int sock = _revs[i].data.fd;

                if (sock == _listensock && (event & EPOLLIN))
                {
                    // listen 获取连接
                    std::string clientip;
                    uint16_t clientport;
                    int fd = Sock::Accept(_listensock, &clientip, &clientport);
                    if (fd < 0)
                    {
                        continue;
                    }
                    struct epoll_event ev;
                    ev.events = EPOLLIN;
                    ev.data.fd = fd;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
                }
                else if (event & EPOLLIN)
                {
                    // 其余sock 且是 read 事件
                    // BUG: 未解决粘包问题
                    char buffer[1024];
                    int n = recv(sock, buffer, sizeof(buffer)-1, 0);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        logMessage(NORMAL, "clienet # %s", buffer);

                        //TODO
                        std::string respond = _func(buffer);
                        send(sock, respond.c_str(), respond.size(), 0);
                    }
                    else if (n == 0)
                    {
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);
                        close(sock);
                        logMessage(NORMAL, "client quit");
                    }
                }
            }
        }

    private:
        uint16_t _port;
        int _listensock;
        int _epfd;
        int _revs_size;
        struct epoll_event* _revs;
        func_t _func;
    };
}
